// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.neuronbit.businessflow.intellij.editor.swing.editor.propertyInspector;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.NlsSafe;
//import com.intellij.uiDesigner.lw.IComponent;
//import com.neuronbit.businessflow.intellij.editor.swing.editor.IProperty;
import com.neuronbit.businessflow.intellij.editor.swing.editor.IProperty;
import com.neuronbit.businessflow.intellij.editor.swing.editor.IComponent;
import com.neuronbit.businessflow.intellij.editor.swing.editor.RadComponent;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

/**
 * The type Property.
 *
 * @param <T> the type parameter
 * @param <V> the type parameter
 */
public abstract class Property<T extends RadComponent, V> implements IProperty {
    /**
     * The constant EMPTY_ARRAY.
     */
    public static final Property[] EMPTY_ARRAY=new Property[]{};

  private static final Logger LOG = Logger.getInstance(Property.class);

  /**
   * Parent property
   */
  private final Property myParent;
  /**
   * Propertie's name
   */
  private final String myName;

    /**
     * Instantiates a new Property.
     *
     * @param parent the parent
     * @param name   the name
     */
    public Property(final Property parent, @NotNull @NonNls final String name) {
    myParent = parent;
    myName = name;
  }

  /**
   * @return property's name.
   */
  @Override
  @NotNull
  public final @NlsSafe String getName() {
    return myName;
  }

  @Override
  public Object getPropertyValue(final IComponent component) {
    //noinspection unchecked
    return getValue((T) component);
  }

    /**
     * This method can extract value of the property from the
     * instance of the RadComponent. This value is passed to the
     * PropertyRenderer and PropertyEditor.
     *
     * @param component the component
     * @return the value
     */
    public abstract V getValue(T component);

    /**
     * Do not invoke this method outside Property class, bacuse
     * {@code setValue(Component,Object)} does some additional work.
     * This method exists only for convenience.
     *
     * @param component the component
     * @param value     the value
     * @throws Exception the exception
     * @see #setValue(RadComponent, Object) #setValue(RadComponent, Object)#setValue(RadComponent,Object)
     */
    protected abstract void setValueImpl(T component, V value) throws Exception;


    /**
     * Sets the {@code value} of the property. This method is invoked
     * after editing is complete.
     *
     * @param component component which property should be set
     * @param value     new propertie's value
     * @throws Exception the exception
     */
    public final void setValue(final T component, final V value) throws Exception{
    setValueImpl(component, value);
    markTopmostModified(component, true);
    //TODO shunyun 2024/3/13:
    //component.getDelegee().invalidate();
  }

    /**
     * Sets value ex.
     *
     * @param component the component
     * @param value     the value
     */
    public final void setValueEx(T component, V value) {
    try {
      setValue(component, value);
    }
    catch(Exception ex) {
      LOG.error(ex);
    }
  }

    /**
     * Mark topmost modified.
     *
     * @param component the component
     * @param modified  the modified
     */
    protected void markTopmostModified(final T component, final boolean modified) {
    Property topmostParent = this;
    while (topmostParent.getParent() != null) {
      topmostParent = topmostParent.getParent();
    }
    if (modified) {
      component.markPropertyAsModified(topmostParent);
    }
    else {
      component.removeModifiedProperty(topmostParent);
    }
  }

    /**
     * Gets parent.
     *
     * @return property which is the parent for this property. The method can return {@code null} if the property doesn't have parent.
     */
    @Nullable
  public final Property getParent() {
    return myParent;
  }

    /**
     * Get children property @ not null [ ].
     *
     * @param component the component
     * @return child properties.
     */
    public Property @NotNull [] getChildren(final T component) {
    return EMPTY_ARRAY;
  }

    /**
     * Gets renderer.
     *
     * @return property 's renderer.
     */
    @NotNull
  public abstract PropertyRenderer<V> getRenderer();

    /**
     * Gets editor.
     *
     * @return property 's editor. The method allows to return {@code null}. In this case property is not editable.
     */
    @Nullable
  public abstract PropertyEditor<V> getEditor();

    /**
     * Applies to boolean.
     *
     * @param component the component
     * @return the boolean
     */
    public boolean appliesTo(T component) {
    return true;
  }

    /**
     * Is modified boolean.
     *
     * @param component the component
     * @return the boolean
     */
    public boolean isModified(final T component) {
    return false;
  }

    /**
     * Reset value.
     *
     * @param component the component
     * @throws Exception the exception
     */
    public void resetValue(T component) throws Exception {
  }

    /**
     * Applies to selection boolean.
     *
     * @param selection the selection
     * @return the boolean
     */
    public boolean appliesToSelection(List<RadComponent> selection) {
    return true;
  }

    /**
     * Need refresh property list boolean.
     *
     * @return the boolean
     */
    public boolean needRefreshPropertyList() {
    return false;
  }
}
